package com.sux.demo;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.timeout.IdleStateHandler;

import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

public class WebSocketClient {
    private NioEventLoopGroup eventExecutors;

    private Channel channel;

    private HeartbeatThread heartbeatThread;

    public WebSocketClient() {
        eventExecutors = new NioEventLoopGroup();
    }

    public Channel getChannel() {
        return channel;
    }

    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss");

    public void connect(String ip, int port, String name) {
        try {
            WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(
                    new URI("ws://" + ip + ":" + port + "/websocket"), WebSocketVersion.V13, null, false, new DefaultHttpHeaders());
            WebSocketClientHandler handler = new WebSocketClientHandler(handshaker);
            ClientHeartbeatHandler heartbeatHandler = new ClientHeartbeatHandler();

            //创建bootstrap对象，配置参数
            Bootstrap bootstrap = new Bootstrap();
            //设置线程组
            bootstrap.group(eventExecutors)
                    //设置客户端的通道实现类型
                    .channel(NioSocketChannel.class)
                    //使用匿名内部类初始化通道
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //添加客户端通道的处理器
                            ch.pipeline().addLast(new HttpClientCodec());
                            ch.pipeline().addLast(new HttpObjectAggregator(65536));
                            ch.pipeline().addLast(new WebSocketClientProtocolHandler(handshaker, true, false));
                            ch.pipeline().addLast(new IdleStateHandler(5, 2, 0, TimeUnit.SECONDS));
                            ch.pipeline().addLast(heartbeatHandler);
                            ch.pipeline().addLast(handler);
                        }
                    });

            // 连接服务端
            ChannelFuture channelFuture = bootstrap.connect(ip, port);

            // 在连接关闭后尝试重连
            channelFuture.channel().closeFuture().addListener(future -> {
                try {
                    if (heartbeatThread != null && heartbeatThread.isAlive()) {
                        System.out.println("停止发送心跳线程");
                        heartbeatThread.close();
                    }
                    Thread.sleep(2000);
                    System.out.println(simpleDateFormat.format(new Date()) + " 重新连接服务端");
                    connect(ip, port, name); // 重新连接服务端
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });

            channelFuture.sync();

            channel = channelFuture.channel();
            System.out.println(simpleDateFormat.format(new Date()) + " " + name + " 已启动");

            // 定时发送心跳
            heartbeatThread = new HeartbeatThread(channel);
            heartbeatThread.start();

            //对通道关闭进行监听
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException | URISyntaxException e) {
            e.printStackTrace();
        } finally {

        }
    }
}
